from cheqed.core import term, formula

class ParseError(Exception):
    def __init__(self, msg):
        Exception.__init__(self, msg)

def variable_of_sexp(sexp):
    if type(sexp) == str:
        return term.Variable(sexp)

    raise ParseError('"%s" not a variable sexp.' % str(sexp))
        
def separation_of_sexp(sexp):
    if sexp[0] != term.Separation.name:
        raise ParseError('Separation sexp must have first item "%s",'
                         ' received "%s" instead.'
                         % (term.Separation.name, str(sexp[0])))

    return term.Separation(variable_of_sexp(sexp[1]),
                           term_of_sexp(sexp[2]),
                           formula_of_sexp(sexp[3]))

def function_of_sexp(sexp):
    return term.Function(sexp[0], *[term_of_sexp(sub) for sub in sexp[1:]])

def term_of_sexp(sexp):
    try:
        return variable_of_sexp(sexp)
    except ParseError:
        pass

    try:
        return separation_of_sexp(sexp)
    except ParseError, p:
        pass

    return function_of_sexp(sexp)

def term_of_string(string):
    return term_of_sexp(sexp_of_string(string))

def variable_of_string(string):
    return variable_of_sexp(sexp_of_string(string))

def formula_of_sexp(sexp):
    if type(sexp) == str:
        return formula.Relation(sexp)

    if sexp[0] == formula.Negation.operator:
        return formula.Negation(formula_of_sexp(sexp[1]))
    
    if sexp[0] == formula.Disjunction.operator:
        return formula.Disjunction(formula_of_sexp(sexp[1]),
                                   formula_of_sexp(sexp[2]))

    if sexp[0] == formula.Conjunction.operator:
        return formula.Conjunction(formula_of_sexp(sexp[1]),
                                   formula_of_sexp(sexp[2]))

    if sexp[0] == formula.Conditional.operator:
        return formula.Conditional(formula_of_sexp(sexp[1]),
                                   formula_of_sexp(sexp[2]))

    if sexp[0] == formula.Biconditional.operator:
        return formula.Biconditional(formula_of_sexp(sexp[1]),
                                     formula_of_sexp(sexp[2]))

    if sexp[0] == formula.Universal.operator:
        return formula.Universal(variable_of_sexp(sexp[1]),
                                 formula_of_sexp(sexp[2]))

    if sexp[0] == formula.Existential.operator:
        return formula.Existential(variable_of_sexp(sexp[1]),
                                   formula_of_sexp(sexp[2]))

    if type(sexp[0]) == str:
        return formula.Relation(sexp[0],
                                *[term_of_sexp(subs) for subs in sexp[1:]])

    raise "invalid formula sexp"

def formula_of_string(string):
    return formula_of_sexp(sexp_of_string(string))
